package com.xh.shuati.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xh.shuati.common.ErrorCode;
import com.xh.shuati.constant.CommonConstant;
import com.xh.shuati.exception.BusinessException;
import com.xh.shuati.exception.ThrowUtils;
import com.xh.shuati.mapper.QuestionBankQuestionMapper;
import com.xh.shuati.model.dto.questionBankQuestion.QuestionBankQuestionQueryRequest;
import com.xh.shuati.model.entity.Question;
import com.xh.shuati.model.entity.QuestionBank;
import com.xh.shuati.model.entity.QuestionBankQuestion;
import com.xh.shuati.model.entity.User;
import com.xh.shuati.model.vo.QuestionBankQuestionVO;
import com.xh.shuati.model.vo.UserVO;
import com.xh.shuati.service.QuestionBankQuestionService;
import com.xh.shuati.service.QuestionBankService;
import com.xh.shuati.service.QuestionService;
import com.xh.shuati.service.UserService;
import com.xh.shuati.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 题目题库关联服务实现
 *
 * @author <a href="https://github.com/canon0521">xh</a>
 */
@Service
@Slf4j
public class QuestionBankQuestionServiceImpl extends ServiceImpl<QuestionBankQuestionMapper, QuestionBankQuestion> implements QuestionBankQuestionService {

    @Resource
    private UserService userService;
    @Resource
    private final QuestionBankService questionBankService;
    @Resource
    private final QuestionService questionService;

    public QuestionBankQuestionServiceImpl(@Lazy QuestionBankService questionBankService, @Lazy QuestionService questionService) {
        this.questionBankService = questionBankService;
        this.questionService = questionService;
    }

    /**
     * 校验数据
     *
     * @param questionBankQuestion
     * @param add                  对创建的数据进行校验
     */
    @Override
    public void validQuestionBankQuestion(QuestionBankQuestion questionBankQuestion, boolean add) {
        ThrowUtils.throwIf(questionBankQuestion == null, ErrorCode.PARAMS_ERROR);

        Long questionBankId = questionBankQuestion.getQuestionBankId();
        Long questionId = questionBankQuestion.getQuestionId();
        if (questionId!=null){
            Question question = questionService.getById(questionId);
            ThrowUtils.throwIf(question==null, ErrorCode.PARAMS_ERROR,"题目不存在");
        }
        if (questionBankId!=null){
            QuestionBank questionBank = questionBankService.getById(questionBankId);
            ThrowUtils.throwIf(questionBank==null, ErrorCode.PARAMS_ERROR,"题库不存在");
        }
        // todo 从对象中取值
//        String title = questionBankQuestion.getTitle();
//        // 创建数据时，参数不能为空
//        if (add) {
//            // todo 补充校验规则
//            ThrowUtils.throwIf(StringUtils.isBlank(title), ErrorCode.PARAMS_ERROR);
//        }
//        // 修改数据时，有参数则校验
//        // todo 补充校验规则
//        if (StringUtils.isNotBlank(title)) {
//            ThrowUtils.throwIf(title.length() > 80, ErrorCode.PARAMS_ERROR, "标题过长");
//        }
    }

    /**
     * 获取查询条件
     *
     * @param questionBankQuestionQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<QuestionBankQuestion> getQueryWrapper(QuestionBankQuestionQueryRequest questionBankQuestionQueryRequest) {
        QueryWrapper<QuestionBankQuestion> queryWrapper = new QueryWrapper<>();
        if (questionBankQuestionQueryRequest == null) {
            return queryWrapper;
        }
        // todo 从对象中取值
        Long id = questionBankQuestionQueryRequest.getId();
        Long questionBankId = questionBankQuestionQueryRequest.getQuestionBankId();
        Long questionId = questionBankQuestionQueryRequest.getQuestionId();
        Long notId = questionBankQuestionQueryRequest.getNotId();
        Long userId = questionBankQuestionQueryRequest.getUserId();
        String sortField = questionBankQuestionQueryRequest.getSortField();
        String sortOrder = questionBankQuestionQueryRequest.getSortOrder();

        // todo 补充需要的查询条件
        // 从多字段中搜索
//        if (StringUtils.isNotBlank(searchText)) {
//            // 需要拼接查询条件
//            queryWrapper.and(qw -> qw.like("title", searchText).or().like("content", searchText));
//        }
//        // 模糊查询
//        queryWrapper.like(StringUtils.isNotBlank(title), "title", title);
//        queryWrapper.like(StringUtils.isNotBlank(content), "content", content);
//        // JSON 数组查询
//        if (CollUtil.isNotEmpty(tagList)) {
//            for (String tag : tagList) {
//                queryWrapper.like("tags", "\"" + tag + "\"");
//            }
//        }
        // 精确查询
        queryWrapper.ne(ObjectUtils.isNotEmpty(notId), "id", notId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(questionBankId), "questionBankId", questionBankId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(questionId), "questionId", questionId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        // 排序规则
        queryWrapper.orderBy(SqlUtils.validSortField(sortField),
                sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 获取题目题库关联封装
     *
     * @param questionBankQuestion
     * @param request
     * @return
     */
    @Override
    public QuestionBankQuestionVO getQuestionBankQuestionVO(QuestionBankQuestion questionBankQuestion, HttpServletRequest request) {
        // 对象转封装类
        QuestionBankQuestionVO questionBankQuestionVO = QuestionBankQuestionVO.objToVo(questionBankQuestion);

        // todo 可以根据需要为封装对象补充值，不需要的内容可以删除
        // region 可选
        // 1. 关联查询用户信息
        Long userId = questionBankQuestion.getUserId();
        User user = null;
        if (userId != null && userId > 0) {
            user = userService.getById(userId);
        }
        UserVO userVO = userService.getUserVO(user);
        questionBankQuestionVO.setUser(userVO);
        // 2. 已登录，获取用户点赞、收藏状态
//        long questionBankQuestionId = questionBankQuestion.getId();
//        User loginUser = userService.getLoginUserPermitNull(request);
//        if (loginUser != null) {
//            // 获取点赞
//            QueryWrapper<QuestionBankQuestionThumb> questionBankQuestionThumbQueryWrapper = new QueryWrapper<>();
//            questionBankQuestionThumbQueryWrapper.in("questionBankQuestionId", questionBankQuestionId);
//            questionBankQuestionThumbQueryWrapper.eq("userId", loginUser.getId());
//            QuestionBankQuestionThumb questionBankQuestionThumb = questionBankQuestionThumbMapper.selectOne(questionBankQuestionThumbQueryWrapper);
//            questionBankQuestionVO.setHasThumb(questionBankQuestionThumb != null);
//            // 获取收藏
//            QueryWrapper<QuestionBankQuestionFavour> questionBankQuestionFavourQueryWrapper = new QueryWrapper<>();
//            questionBankQuestionFavourQueryWrapper.in("questionBankQuestionId", questionBankQuestionId);
//            questionBankQuestionFavourQueryWrapper.eq("userId", loginUser.getId());
//            QuestionBankQuestionFavour questionBankQuestionFavour = questionBankQuestionFavourMapper.selectOne(questionBankQuestionFavourQueryWrapper);
//            questionBankQuestionVO.setHasFavour(questionBankQuestionFavour != null);
//        }
        // endregion

        return questionBankQuestionVO;
    }

    /**
     * 分页获取题目题库关联封装
     *
     * @param questionBankQuestionPage
     * @param request
     * @return
     */
    @Override
    public Page<QuestionBankQuestionVO> getQuestionBankQuestionVOPage(Page<QuestionBankQuestion> questionBankQuestionPage, HttpServletRequest request) {
        List<QuestionBankQuestion> questionBankQuestionList = questionBankQuestionPage.getRecords();
        Page<QuestionBankQuestionVO> questionBankQuestionVOPage = new Page<>(questionBankQuestionPage.getCurrent(), questionBankQuestionPage.getSize(), questionBankQuestionPage.getTotal());
        if (CollUtil.isEmpty(questionBankQuestionList)) {
            return questionBankQuestionVOPage;
        }
        // 对象列表 => 封装对象列表
        List<QuestionBankQuestionVO> questionBankQuestionVOList = questionBankQuestionList.stream().map(questionBankQuestion -> {
            return QuestionBankQuestionVO.objToVo(questionBankQuestion);
        }).collect(Collectors.toList());

        // todo 可以根据需要为封装对象补充值，不需要的内容可以删除
        // region 可选
        // 1. 关联查询用户信息
        Set<Long> userIdSet = questionBankQuestionList.stream().map(QuestionBankQuestion::getUserId).collect(Collectors.toSet());
        Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()
                .collect(Collectors.groupingBy(User::getId));
        // 2. 已登录，获取用户点赞、收藏状态
//        Map<Long, Boolean> questionBankQuestionIdHasThumbMap = new HashMap<>();
//        Map<Long, Boolean> questionBankQuestionIdHasFavourMap = new HashMap<>();
//        User loginUser = userService.getLoginUserPermitNull(request);
//        if (loginUser != null) {
//            Set<Long> questionBankQuestionIdSet = questionBankQuestionList.stream().map(QuestionBankQuestion::getId).collect(Collectors.toSet());
//            loginUser = userService.getLoginUser(request);
//            // 获取点赞
//            QueryWrapper<QuestionBankQuestionThumb> questionBankQuestionThumbQueryWrapper = new QueryWrapper<>();
//            questionBankQuestionThumbQueryWrapper.in("questionBankQuestionId", questionBankQuestionIdSet);
//            questionBankQuestionThumbQueryWrapper.eq("userId", loginUser.getId());
//            List<QuestionBankQuestionThumb> questionBankQuestionQuestionBankQuestionThumbList = questionBankQuestionThumbMapper.selectList(questionBankQuestionThumbQueryWrapper);
//            questionBankQuestionQuestionBankQuestionThumbList.forEach(questionBankQuestionQuestionBankQuestionThumb -> questionBankQuestionIdHasThumbMap.put(questionBankQuestionQuestionBankQuestionThumb.getQuestionBankQuestionId(), true));
//            // 获取收藏
//            QueryWrapper<QuestionBankQuestionFavour> questionBankQuestionFavourQueryWrapper = new QueryWrapper<>();
//            questionBankQuestionFavourQueryWrapper.in("questionBankQuestionId", questionBankQuestionIdSet);
//            questionBankQuestionFavourQueryWrapper.eq("userId", loginUser.getId());
//            List<QuestionBankQuestionFavour> questionBankQuestionFavourList = questionBankQuestionFavourMapper.selectList(questionBankQuestionFavourQueryWrapper);
//            questionBankQuestionFavourList.forEach(questionBankQuestionFavour -> questionBankQuestionIdHasFavourMap.put(questionBankQuestionFavour.getQuestionBankQuestionId(), true));
//        }
        // 填充信息
        questionBankQuestionVOList.forEach(questionBankQuestionVO -> {
            Long userId = questionBankQuestionVO.getUserId();
            User user = null;
            if (userIdUserListMap.containsKey(userId)) {
                user = userIdUserListMap.get(userId).get(0);
            }
            questionBankQuestionVO.setUser(userService.getUserVO(user));
//            questionBankQuestionVO.setHasThumb(questionBankQuestionIdHasThumbMap.getOrDefault(questionBankQuestionVO.getId(), false));
//            questionBankQuestionVO.setHasFavour(questionBankQuestionIdHasFavourMap.getOrDefault(questionBankQuestionVO.getId(), false));
        });
        // endregion

        questionBankQuestionVOPage.setRecords(questionBankQuestionVOList);
        return questionBankQuestionVOPage;
    }



    /***
     * 批量添加题目到题库 事务操作 仅内部调用
     * @param questionBankQuestionList
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchAddQuestionsToBankInner(List<QuestionBankQuestion> questionBankQuestionList){
//        for (QuestionBankQuestion questionBankQuestion : questionBankQuestionList){
//            //细粒度异常报错，避免数据库连接问题、事务问题等导致操作失败
//            try {
//                boolean result = this.save(questionBankQuestion);
//                if (!result) {
//                    throw new BusinessException(ErrorCode.OPERATION_ERROR, "向题库添加题目失败");
//                }
//            } catch (DataIntegrityViolationException e) {
//                log.error("数据库唯一键冲突或违反其他完整性约束，题目 id: {}, 题库 id: {}, 错误信息: {}",
//                        questionBankQuestion.getQuestionId(), questionBankQuestion.getQuestionBankId(), e.getMessage());
//                throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目已存在于该题库，无法重复添加");
//            } catch (DataAccessException e) {
//                log.error("数据库连接问题、事务问题等导致操作失败，题目 id: {}, 题库 id: {}, 错误信息: {}",
//                        questionBankQuestion.getQuestionId(), questionBankQuestion.getQuestionBankId(), e.getMessage());
//                throw new BusinessException(ErrorCode.OPERATION_ERROR, "数据库操作失败");
//            } catch (Exception e) {
//                // 捕获其他异常，做通用处理
//                log.error("添加题目到题库时发生未知错误，题目 id: {}, 题库 id: {}, 错误信息: {}",
//                        questionBankQuestion.getQuestionId(), questionBankQuestion.getQuestionBankId(), e.getMessage());
//                throw new BusinessException(ErrorCode.OPERATION_ERROR, "向题库添加题目失败");
//            }
//        }
        try {
            boolean result = this.saveBatch(questionBankQuestionList);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "向题库添加题目失败");
        } catch (DataIntegrityViolationException e) {
            log.error("数据库唯一键冲突或违反其他完整性约束, 错误信息: {}", e.getMessage());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目已存在于该题库，无法重复添加");
        } catch (DataAccessException e) {
            log.error("数据库连接问题、事务问题等导致操作失败, 错误信息: {}", e.getMessage());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "数据库操作失败");
        } catch (Exception e) {
            // 捕获其他异常，做通用处理
            log.error("添加题目到题库时发生未知错误，错误信息: {}", e.getMessage());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "向题库添加题目失败");
        }


    }

    //批处理分批，避免回滚
    @Override
    public void batchAddQuestionsToBank(List<Long> questionIds, Long bankId, User loginUser) {
        //参数校验
        ThrowUtils.throwIf(questionIds == null || questionIds.isEmpty(), ErrorCode.PARAMS_ERROR);
        //判断用户是否登录
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN);

        //检查题库是否存在
        //QuestionBank questionBank = questionBankService.getById(bankId); 避免使用select *
        LambdaQueryWrapper<QuestionBank> questionBankLambdaQueryWrapper = Wrappers.lambdaQuery(QuestionBank.class).select(QuestionBank::getId);
        QuestionBank questionBank = questionBankService.getOne(questionBankLambdaQueryWrapper.eq(QuestionBank::getId, bankId));
        ThrowUtils.throwIf(questionBank == null, ErrorCode.NOT_FOUND, "题库id不存在");

        //检查题目是否存在
        //List<Question> questionList = questionService.listByIds(questionIds);
        LambdaQueryWrapper<Question> questionLambdaQueryWrapper = Wrappers.lambdaQuery(Question.class).select(Question::getId).in(Question::getId, questionIds);
        List<Long> validIdList = questionService.listObjs(questionLambdaQueryWrapper, obj->(Long) obj);
//        List<Long> validIdList = questionList.stream().map(Question::getId).collect(Collectors.toList());
        ThrowUtils.throwIf(CollUtil.isEmpty(validIdList), ErrorCode.PARAMS_ERROR, "有题目不存在");

        //检查哪些题目已经题库中，避免重复插入
        LambdaQueryWrapper<QuestionBankQuestion> questionBankQuestionLambdaQueryWrapper = Wrappers.lambdaQuery(QuestionBankQuestion.class)
                .eq(QuestionBankQuestion::getQuestionId, questionIds)
                .in(QuestionBankQuestion::getQuestionId, validIdList);
        //已存在的题目
        List<QuestionBankQuestion> existingQuestionList = this.list(questionBankQuestionLambdaQueryWrapper);
        Set<Long> existingIdSet = existingQuestionList.stream().map(QuestionBankQuestion::getQuestionId).collect(Collectors.toSet());

        //把validIdList中的existingIdSet进行过滤
        validIdList = validIdList.stream().filter(id -> {
            return !existingIdSet.contains(id);
        }).collect(Collectors.toList());
        ThrowUtils.throwIf(CollUtil.isEmpty(validIdList), ErrorCode.PARAMS_ERROR, "所有题目都在题库中，无需重复插入");


        // 自定义线程池 （IO 密集型任务）
        ThreadPoolExecutor customExecutor = new ThreadPoolExecutor(
                20,                         // 核心线程数
                50,                        // 最大线程数
                60L,                       // 线程空闲存活时间
                TimeUnit.SECONDS,           // 存活时间单位
                new LinkedBlockingQueue<>(1000),  // 阻塞队列容量
                new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略：由调用线程处理任务
        );

        // 保存所有批次任务
        List<CompletableFuture<Void>> futures = new ArrayList<>();

        //分批处理：wq::w
        int batchSize = 1;
        int total = validIdList.size();
        for (int i = 0; i < total; i += batchSize){
            int end = Math.min(i + batchSize, total);
            List<Long> subList = validIdList.subList(i, end);
            //使用事务处理每一批数据 直接调用 事务会失效
//            this.batchAddQuestionsToBankInner(subList.stream().map(id -> {
//                QuestionBankQuestion  questionBankQuestion = new QuestionBankQuestion();
//                questionBankQuestion.setQuestionBankId(bankId);
//                questionBankQuestion.setQuestionId(id);
//                questionBankQuestion.setUserId(loginUser.getId());
//                return questionBankQuestion;
//            }).collect(Collectors.toList()));


            //获取代理对象
            QuestionBankQuestionService proxy = (QuestionBankQuestionServiceImpl) AopContext.currentProxy();
            List<QuestionBankQuestion> questionBankQuestionList = subList.stream().map(id -> {
                QuestionBankQuestion questionBankQuestion = new QuestionBankQuestion();
                questionBankQuestion.setQuestionBankId(bankId);
                questionBankQuestion.setQuestionId(id);
                questionBankQuestion.setUserId(loginUser.getId());
                return questionBankQuestion;
            }).collect(Collectors.toList());

            //异步处理每一批数据
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                proxy.batchAddQuestionsToBankInner(questionBankQuestionList);
            }, customExecutor).exceptionally(ex -> {
                log.error("批处理任务执行失败", ex);
                return null;
            });

            //将任务添加到异步任务列表
            futures.add(future);
        }
        //等待所有任务完成操作
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

        //执行完成后关闭线程池，释放资源
        customExecutor.shutdown();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchRemoveQuestionsToBank(List<Long> questionIds, Long bankId) {
        //参数校验
        ThrowUtils.throwIf(questionIds == null || questionIds.isEmpty(), ErrorCode.PARAMS_ERROR);

        //执行删除
        for (Long questionId : questionIds) {
            LambdaQueryWrapper<QuestionBankQuestion> lambdaQueryWrapper = Wrappers.lambdaQuery(QuestionBankQuestion.class).eq(QuestionBankQuestion::getQuestionId, questionId).eq(QuestionBankQuestion::getQuestionBankId, bankId);
            boolean isremove = this.remove(lambdaQueryWrapper);
            ThrowUtils.throwIf(!isremove, ErrorCode.OPERATION_ERROR,  "从题库移除题目失败");
        }
    }

}
